package com.spc.boot.admin.util;

import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * Title: 时间日期辅助类
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * Copyright: Copyright (c)
 * </p>
 * <p>
 * Company:
 * </p>
 * <p>
 * CreateTime:
 *
 * @author
 * @version 1.0
 */
public class DateUtil {

    /**
     * 默认的日期样式：yyyy-MM-dd
     */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

    public static final int YEAR = 1;

    public static final int MONTH = 2;

    public static final int DAY = 3;

    public static final int HOUR = 4;

    public static final int MINUTE = 5;

    public static final int SECOND = 6;

    public DateUtil() {
    }

    /**
     * 将日期字符串生成日期类 内部格式有"yyyy-MM-dd HH:mm:ss","yyyy-MM-ddHH:mm:ss","yyyy/MM/dd
     * HH:mm:ss", "yyyy-M-d HH:mm:ss", "yyyy/M/d HH:mm:ss","yyyyMMddHHmmss"
     *
     * @param date
     * @param defaultValue return Date
     */
    @SuppressWarnings("deprecation")
    public static Date parseDateLoosely(String date, Date defaultValue) {
        if (date == null)
            return defaultValue;

        String[] dateTimePatterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss",
                "yyyy-M-d HH:mm:ss", "yyyy/M/d HH:mm:ss", "yyyyMMddHHmmss"};
        String[] datePatterns = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy-M-d", "yyyy/M/d"};
        String[] timePatterns = {"HH:mm:ss", "HH:mm"};

        String[] patterns;
        int dateSplit = date.indexOf("/");
        if (dateSplit == -1)
            dateSplit = date.indexOf("-");

        int timeSplit = date.indexOf(":");

        if (dateSplit == -1 && timeSplit != -1) {
            patterns = timePatterns;
        } else if (dateSplit != -1 && timeSplit == -1) {
            patterns = datePatterns;
        } else {
            patterns = dateTimePatterns;
        }

        SimpleDateFormat df = new SimpleDateFormat();
        Date rt = null;
        for (int i = 0; i < patterns.length; i++) {
            df.applyPattern(patterns[i]);
            try {
                rt = df.parse(date);
            } catch (ParseException e) {
            }
            if (rt != null)
                return rt;
        }
        if (rt == null) {
            try {
                rt = new Date(date);
                if (rt != null)
                    return rt;
            } catch (Exception e) {
            }
        }

        return defaultValue;
    }

    private static Pattern datePattern = Pattern.compile("(?<year>\\d{4})[年/-](?<month>\\d{1,2})[月/-](?<day>\\d{1,2})日?( (?<hour>\\d{1,2}):(?<min>\\d{1,2}):(?<second>\\d{1,2}))?");

    public static LocalDateTime parseLocalDateTimeLoosely(String date, LocalDateTime defaultValue) {
        if (date == null || date.trim().length() == 0)
            return defaultValue;

        Matcher macther = datePattern.matcher(date);
        if (!macther.matches())
            throw new RuntimeException(date + "并非时间格式。");

        int year = Integer.parseInt(macther.group("year"));
        int month = Integer.parseInt(macther.group("month"));
        int day = Integer.parseInt(macther.group("day"));
        int hour = 0;
        int min = 0;
        int second = 0;
        if (macther.group("hour") != null) {
            hour = Integer.parseInt(macther.group("hour"));
            min = Integer.parseInt(macther.group("min"));
            second = Integer.parseInt(macther.group("second"));
        }

        return LocalDateTime.of(year, month, day, hour, min, second);
    }

    public static LocalDate parseLocalDateLoosely(String date, LocalDate defaultValue) {
        if (date == null || date.trim().length() == 0)
            return defaultValue;

        Matcher macther = datePattern.matcher(date);
        if (!macther.matches())
            throw new RuntimeException(date + "并非时间格式。");

        int year = Integer.parseInt(macther.group("year"));
        int month = Integer.parseInt(macther.group("month"));
        int day = Integer.parseInt(macther.group("day"));

        return LocalDate.of(year, month, day);
    }

    /**
     * 将日期字串生成指定格式的日期
     *
     * @param date
     * @param pattern
     * @param defaultValue return Date
     */
    public static Date parseDateWithPattern(String date, String pattern, Date defaultValue) {
        SimpleDateFormat df = new SimpleDateFormat(pattern);
        try {
            if (!StringUtils.isEmpty(date))
                return df.parse(date);
            else
                return defaultValue;
        } catch (ParseException e) {
            return defaultValue;
        }
    }

    /**
     * 判断是否为正确的时间日期字符串
     *
     * @param in     String : 待判断的字符串，如：1980-01-01 10:10:10
     * @param format String : 时间日期格式，如：yyyy-MM-dd HH:mm:ss
     * @return boolean : 如果正确则返回true，否则返回false
     */
    public static boolean isDateFormat(final String in, final String format) {
        if (in == null)
            return false;
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            Date date = new Date(formatter.parse(in).getTime());
            // 非法时间格式
            if (toString(date, format).equals(in) == false)
                return false;
            return true;
        } catch (ParseException pe) {
            return false;
        }
    }

    /**
     * 判断是否为正确的时间日期字符串
     *
     * @param in String : 待判断的字符串，如：1980-01-01（符合yyyy-MM-dd格式）
     * @return boolean : 如果正确则返回true，否则返回false
     */
    public static boolean isDateFormat(final String in) {
        return isDateFormat(in, DEFAULT_DATE_FORMAT);
    }

    /**
     * 将字符串转换为java.sql.Date
     *
     * @param in     String : 待转换的字符串，如：1980-01-01 10:10:10
     * @param format String : 日期格式，如：yyyy-MM-dd HH:mm:ss
     * @return Date : 转换后的日期，若字符串表示的是非法时间格式，则返回null
     */
    public static Date toDate(final String in, final String format) {
        if (isDateFormat(in, format) == false)
            return null;
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            return formatter.parse(in);
        } catch (ParseException pe) {
            return null;
        }
    }

    /**
     * 从Timestamp转换为Date
     *
     * @param in java.sql.Timestamp : 时间
     * @return java.util.Date :
     */
    public static Date fromTimestamp(final Timestamp in) {
        if (in == null)
            return null;
        return new Date(in.getTime());
    }

    /**
     * 将Timestamp格式化输出
     *
     * @param in     Timestamp : 要转换成字符串的日期
     * @param format : 格式，请参照java.text.SimpleDateFormat的格式
     * @return String : 转换后的字符串形日期
     */
    public static String toString(final Timestamp in, final String format) {
        return toString(fromTimestamp(in), format);
    }

    /**
     * 将Date格式化输出
     *
     * @param in     Date : 要转换成字符串的日期
     * @param format : 格式，请参照java.text.SimpleDateFormat的格式
     * @return String : 转换后的字符串形日期
     */
    public static String toString(final Date in, final String format) {
        if (in == null)
            return null;
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            return formatter.format(in);
        } catch (Exception e) {
            SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
            return formatter.format(in);
        }
    }

    /**
     * 取得某年某月的天数
     *
     * @param year  int : 年份（大于0）
     * @param month int : 月份
     * @return int : 年月所对应的天数
     */
    public static int getDayCounts(final int year, final int month) {
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                return 31;
            case 2:
                if (isLeapYear(year)) {
                    return 29;
                } else {
                    return 28;
                }
            case 4:
            case 6:
            case 9:
            case 11:
                return 30;
            default:
                return 0;
        }
    }

    /**
     * 是否为星期天
     *
     * @param year  int : 年份
     * @param month int : 月份
     * @param day   int : 天
     * @return boolean : 如果是星期天返回true,否则返回false
     */
    public static boolean isSunday(final int year, final int month, final int day) {
        return (getDayofWeek(year, month, day) == 1);
    }

    /**
     * 是否为星期六
     *
     * @param year  int : 年份
     * @param month int : 月份
     * @param day   int : 天
     * @return boolean : 如果是星期六返回true,否则返回false
     */
    public static boolean isSaturday(final int year, final int month, final int day) {
        return (getDayofWeek(year, month, day) == 7);
    }

    /**
     * 是否为周末(星期六，日)
     *
     * @param year  int : 年份
     * @param month int : 月份
     * @param day   int : 天
     * @return boolean : 如果是周末则返回true,否则返回false
     */
    public static boolean isWeekend(final int year, final int month, final int day) {
        return (isSaturday(year, month, day) || isSunday(year, month, day));
    }

    private static int getDayofWeek(int year, int month, int day) {
        Calendar c = Calendar.getInstance();
        c.set(year, month - 1, day);
        return c.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * 得到日期在一周的天数
     *
     * @param date Date : 具体日期
     * @return int : 周几
     */
    public static int getDayofWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_WEEK) - 1;
    }

    /**
     * 得到星期几(中文)
     *
     * @param date Date : 具体日期
     * @return int : 周几
     */
    public static String getDate(Date date) {
        int dayofWeek = getDayofWeek(date);
        switch (dayofWeek) {
            case 1:
                return "一";
            case 2:
                return "二";
            case 3:
                return "三";
            case 4:
                return "四";
            case 5:
                return "五";
            case 6:
                return "六";
            case 0:
                return "日";

            default:
                return null;
        }
    }

    /**
     * 判断某年份是否为闰年
     *
     * @param year int : 年份（大于0）
     * @return boolean : 如果是返回true，否则返回false
     */
    public static boolean isLeapYear(final int year) {
        if (year <= 0)
            return false;
        if (year % 4 != 0)
            return false;
        if (year % 100 == 0 && year % 400 != 0)
            return false;
        return true;
    }

    /**
     * 得到当前时间的年份
     *
     * @return int : 当前年份
     */
    public static int getNowYear() {
        Date date = new Date(getSystemTime());
        return get(date, YEAR);
    }

    /**
     * 得到当前时间的月份
     *
     * @return int : 当前月份：[1,12]
     */
    public static int getNowMonth() {
        Date date = new Date(getSystemTime());
        return get(date, MONTH);
    }

    /**
     * 得到当前时间的日期
     *
     * @return int : 当前日期：[1,31]
     */
    public static int getNowDay() {
        Date date = new Date(getSystemTime());
        return get(date, DAY);
    }

    /**
     * 得到当前时间的小时
     *
     * @return int : 当前小时：[0,23]
     */
    public static int getNowHour() {
        Date date = new Date(getSystemTime());
        return get(date, HOUR);
    }

    /**
     * 得到当前时间的分钟数
     *
     * @return int : 当前分钟：[0,59]
     */
    public static int getNowMinute() {
        Date date = new Date(getSystemTime());
        return get(date, MINUTE);
    }

    /**
     * 得到当前时间的秒钟数
     *
     * @return int : 当前秒种：[0,59]
     */
    public static int getNowSecond() {
        Date date = new Date(getSystemTime());
        return get(date, SECOND);
    }

    /**
     * 对时间作调整
     *
     * @param date   Date : 需要做调整的时间日期
     * @param field  int : 需要调整的项，
     * @param amount int : 调整量（可以为负数）
     * @return Date : 调整后的时间
     */
    public static Date add(final Date date, final int field, final int amount) {
        if (date == null)
            return null;
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(date.getTime());
        switch (field) {
            case YEAR:
                c.add(Calendar.YEAR, amount);
                break;
            case MONTH: // 1 - 12
                c.add(Calendar.MONTH, amount);
                break;
            case DAY:
                c.add(Calendar.DATE, amount);
                break;
            case HOUR:
                c.add(Calendar.HOUR, amount);
                break;
            case MINUTE:
                c.add(Calendar.MINUTE, amount);
                break;
            case SECOND:
                c.add(Calendar.SECOND, amount);
                break;
            default:
                return null;
        }
        return new Date(c.getTimeInMillis());
    }

    /**
     * 对时间作调整
     *
     * @param date          需要做调整的时间日期
     * @param fieldVal(Map) 调整量（可以为负数） 是位置与值的表put(DateHelper.MINUTE,25);
     * @return
     */
    public static Date add(final Date date, final Map<Integer, Integer> fieldVal) {
        Date newDate = date;
        for (Iterator<Integer> iter = fieldVal.keySet().iterator(); iter.hasNext(); ) {
            Integer field = (Integer) iter.next();
            newDate = add(newDate, field, (Integer) fieldVal.get(field));
        }
        return newDate;
    }

    /**
     * 取得某日期各个位置的值
     *
     * @param date  Date : 日期
     * @param field int : 位置，具体参数本类的静态参数
     * @return int : 相应位置的值
     */
    public static int get(final Date date, final int field) {
        if (date == null) {
            return 0;
        }
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(date.getTime());
        switch (field) {
            case YEAR:
                return c.get(Calendar.YEAR);
            case MONTH: // 1 - 12
                return c.get(Calendar.MONTH) + 1;
            case DAY:
                return c.get(Calendar.DAY_OF_MONTH);
            case HOUR:
                return c.get(Calendar.HOUR_OF_DAY);
            case MINUTE:
                return c.get(Calendar.MINUTE);
            case SECOND:
                return c.get(Calendar.SECOND);
            default:
                return 0;
        }
    }

    /**
     * 根据给出的指定日期 和想得到的 日期段格式 得到目标值
     *
     * @param date
     * @param format
     * @return
     * @author ghliu
     */
    public static Date get(final Date date, final String format) {
        String dateStr = toString(date, format);
        return parseDateWithPattern(dateStr, format, new Date());
    }

    public static Timestamp toTimestamp(Date in) {
        if (in == null)
            return null;
        return new Timestamp(in.getTime());
    }

    public static Date toDate(Timestamp in) {
        if (in == null)
            return null;
        return new Date(in.getTime());
    }

    /**
     * 得到指定年，月，日，时，分，秒的时间的毫秒数
     *
     * @param year   int : 年[0..)
     * @param month  int : 月[0..11]
     * @param day    int : 日[1..31]
     * @param hour   int : 时[0..23]
     * @param min    int : 分[0..59]
     * @param second int : 秒[0..59]
     * @return long : 毫秒数
     */
    public static long getTime(final int year, final int month, final int day, final int hour, final int min,
                               final int second) {
        Calendar c = Calendar.getInstance();
        c.set(year, month, day, hour, min, second);
        return c.getTimeInMillis();
    }

    /**
     * 取得当前系统时间（WEB服务器）
     *
     * @return long : 系统当前时间的毫妙数
     */
    public static long getSystemTime() {
        return System.currentTimeMillis();
    }

    /**
     * 取得当前系统时间（WEB服务器）
     *
     * @param format String : 时间显示的格式
     * @return String : 用字符串表示的时间串
     */
    public static String getSystemTime(final String format) {
        return toString(new Date(getSystemTime()), format);
    }

    /**
     * 得到今天的日期
     *
     * @return Date : 日期
     */
    public static Date getToday() {
        return DateUtil.toDate(DateUtil.getSystemTime(DEFAULT_DATE_FORMAT), DEFAULT_DATE_FORMAT);
    }

    /**
     * 判断是否在当前日期之前
     *
     * @param date String : 格式：yyyy-MM-dd
     * @return boolean :
     */
    public static boolean isBeforeToday(final String date) {
        Date d = DateUtil.toDate(date, DEFAULT_DATE_FORMAT);
        if (d == null)
            return false;
        return d.before(getToday());
    }

    /**
     * 判断是否在当前日期之后
     *
     * @param date String : 格式：yyyy-MM-dd
     * @return boolean :
     */
    public static boolean isAfterToday(final String date) {
        Date d = DateUtil.toDate(date, DEFAULT_DATE_FORMAT);
        if (d == null)
            return false;
        return d.after(getToday());
    }

    /**
     * 判断是否为今天
     *
     * @param date String : 格式：yyyy-MM-dd
     * @return boolean :
     */
    public static boolean isToday(final String date) throws Exception {
        Date d = DateUtil.toDate(date, DEFAULT_DATE_FORMAT);
        if (d == null)
            return false;
        return (d.getTime() == getToday().getTime());
    }

    /**
     * 取得当前时间点所对应的时间段（xx:00~xx:00）
     *
     * @return String
     */
    public static String getHoursRange(int hours) {
        Integer hoursAdd1 = hours + 1;
        String hoursRange = Integer.toString(hours) + ":" + "00" + "~" + hoursAdd1.toString() + ":" + "00";
        return hoursRange;
    }

    /**
     * 返回一个日期是第几周
     *
     * @param date
     * @return
     */

    public static int getWeekOfYearFromDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
        return cal.get(Calendar.WEEK_OF_YEAR);

    }

    public static String CNDateString(Date date) {
        String[] cn = new String[]{"〇", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
        String year = get(date, YEAR) + "";
        StringBuffer str = new StringBuffer();
        for (int i = 0; i < year.length(); i++) {
            str.append(cn[Integer.parseInt(year.charAt(i) + "")]);
        }
        str.append("年");
        int MM = get(date, MONTH);
        if (MM < 10) {
            str.append(cn[MM]);
        } else if (MM < 20) {
            str.append("十" + cn[MM % 10]);
        }
        str.append("月");

        int dd = get(date, DAY);
        if (dd < 10) {
            str.append(cn[dd]);
        } else if (dd < 20) {
            str.append("十" + cn[dd % 10]);
        } else {
            str.append("二十" + cn[dd % 10]);
        }
        str.append("日");
        return str.toString();
    }

    /**
     * 判断2个时间大小
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean diffDate(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false;
        }

        if (date2.getTime() > date1.getTime()) {
            return true;
        } else {
            return false;
        }

    }

    //===============================> Date 与 LocalDateTime 互转

    // 01. java.util.Date --> java.time.LocalDateTime
    public static LocalDateTime DateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }


    // 04. java.time.LocalDateTime --> java.util.Date
    public static Date LocalDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    // 01. java.util.Date --> java.time.LocalDateTime
    public static LocalDate dateToLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }


    // 04. java.time.LocalDateTime --> java.util.Date
    public static Date localDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    public static LocalTime dateToLocalTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }

    public static Date localDateTimeToDate(LocalDateTime localDateTime){
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 格式化{@link LocalDateTime}
     *
     * @param srcTime
     *            时间
     * @param format
     *            格式
     * @return 格式化后的字符串
     */
    public static String formatLocalDateTime(LocalDateTime srcTime, String format) {
        if (srcTime == null) {
            return null;
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return srcTime.format(formatter);
    }

    /**
     * 格式化{@link LocalDateTime}
     *
     * @param srcTime
     *            时间
     * @param format
     *            格式
     * @return 格式化后的字符串
     */
    public static String formatLocalDate(LocalDate srcTime, String format) {
        if (srcTime == null) {
            return null;
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return srcTime.format(formatter);
    }

    /**
     * 获取包含毫秒的时间
     *
     * @return 获取包含毫秒的时间
     */
    public static LocalDateTime getLocalDateTimeWithNano() {
        LocalDateTime dtNow = LocalDateTime.now();
        return dtNow.withNano(dtNow.getNano());
    }
}